/**
  ETFAna project, Anyang Normal University && IMP-CAS
  \class ETFVClient
  \brief client for communication among different hosts on the Internet
  NOTE that this is an abstract class
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2023-06-09
  \date 2023-06-09 last modified
  \attention
  changelog
  <table>
  <tr>  <th>Date         <th>Author      <th>Description                    </tr>
  <tr>  <td>2023-06-09   <td>Asia Sun    <td>file created                   </tr>
  </table>

  \copyright Copyright (c) 2021-2024 Anyang Normal U. && IMP-CAS with LGPLv3 LICENSE
*/

#include <iostream>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include "ETFVClient.h"
#include "ETFQueue.h"
#include "ETFMsg.h"

using std::cout;
using std::endl;

#define er ETFMsg::Error

ETFVClient::ETFVClient() : ETFDaqData(), fd(-1){}

/// establish connection with ip:port
ETFVClient::ETFVClient(const string &ip, short port, DaqType daqType)
: ETFDaqData(daqType), fIP(ip), fPort(port), fd(-1){
  fIsConnected = Connect();
} // end ctor

ETFVClient::~ETFVClient(){
  Close();
} // end dtor

// establish connection with ip:port
bool ETFVClient::Connect(){
  // fd = socket(AF_INET, SOCK_STREAM, 0);
  fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); // NON_BLOCK
  if(-1 == fd) er("ETFVClient", "Connect: could not create socket: %s", strerror(errno));
  ETFMsg::Info("ETFVClient", "Connect(%s): socket created, file-dscpt: %d", fDaqC, fd);

  fSock.sin_family = AF_INET;
  fSock.sin_addr.s_addr = inet_addr(fIP.c_str());
  fSock.sin_port = htons(fPort); // host to net (short) byte order

  // time out waiting for the emergence of the object server //
  const int DT = 200;
  time_t t0 = ETFMsg::ms(), t1 = t0, dt = DT;
  ETFMsg::Info("ETFVClient", "Connect(%s): Waiting for a server", fDaqC);
  while(-1 == connect(fd, (sockaddr *)&fSock, sizeof(fSock))){
    if(ETFMsg::ms() - t1 >= 50){ // print every 0.05 s
      cout << "About to time out: " << dt << "ms\r" << std::flush;
      dt -= 100; t1 = ETFMsg::ms();
    } // end if
    if(ETFMsg::ms() - t0 > DT){
      ETFMsg::Info("ETFVClient(%s)", "Connection timed out, abort...", fDaqC);
      return false;
    } // end if
    if(ETFMsg::irp()) return false;
  } // end while
  ETFMsg::Info("ETFVClient", "Connect(%s): connection to %s:%d established",
    fDaqC, fIP.data(), fPort);

  // read the greeting of the server to acknowledge the connection //
  char m0[128] = "";
  ssize_t s = 0, p = 0;
  while(1){ // unblock read
    s = read(fd, m0 + p, sizeof(m0) - p);
    if(-1 == s && EAGAIN == errno) continue;
    if(s > 0) p += s;
    if(size_t(p) <= sizeof(m0)) break;
  } // end while
  if(-1 == s) er("ETFVClient", "Connect: reading ack error: %s", strerror(errno));
  puts(m0);
  return true;
} // end member function Connect

// thread func to read from LAN
void *ETFVClient::avatar(void *arg){
  ETFVClient *vc = static_cast<ETFVClient *>(arg);
  while(!ETFMsg::irp()) vc->recv();
  return nullptr;
} // end member function avatar
void ETFVClient::Initialize(){
  const int s = pthread_create(&fRecv, nullptr, ETFVClient::avatar, this);
  fIsRecvThAlive = true;
  if(0 != s) er("ETFVClient(%s)", "Initialize: pthread_create err - %s", fDaqC, strerror(s));
  ETFMsg::Info("ETFVClient(%s)", "Initialize: the receive thread is now online", fDaqC);
} // end member function Initialize

// do some clean-ups
void ETFVClient::Close(){
  // close the recv thread //
  if(fIsRecvThAlive){
    pthread_cancel(fRecv);
    const int s = pthread_join(fRecv, nullptr);
    if(0 != s) er("ETFVClient", "Close: pthread_join err - %s", strerror(s));
    fIsRecvThAlive = false;
  } // end if
  // close the sock //
  if(fd >= 0){ close(fd); fd = -1; }

  printf("\033[36;1mThis is %s client. Bye~\033[0m\n", fDaqC);
  if(fQueue){ delete fQueue; fQueue = nullptr; }
} // end member function Close
